<template>
  <transition :name="clsPrefix + 'zoom-in-top'" @after-leave="$emit('dodestroy')">
    <div
      v-show="visible"
      :class="[prefixCls, clsPrefix + 'popper', popperClass]">
      <div :class="[prefixCls + '__content', { 'has-seconds': showSeconds }]">
        <time-spinner
          ref="spinner"
          @change="handleChange"
          :arrow-control="useArrow"
          :show-seconds="showSeconds"
          :am-pm-mode="amPmMode"
          @select-range="setSelectionRange"
          :date="date">
        </time-spinner>
      </div>
      <div :class="[prefixCls + '__footer']">
        <button
          type="button"
          :class="[prefixCls + '__btn', 'cancel']"
          @click="handleCancel">{{ t(localePrefix + 'cancel') }}</button>
        <button
          type="button"
          :class="[prefixCls + '__btn', { 'confirm': !disabled}]"
          @click="handleConfirm()">{{ t(localePrefix + 'confirm') }}</button>
      </div>
    </div>
  </transition>
</template>

<script type="text/babel">
import Config from '../../../config'
import { limitTimeRange, isDate, clearMilliseconds, timeWithinRange } from '../util'
import Locale from '../../../mixins/locale'
import TimeSpinner from '../basic/time-spinner'

const clsPrefix = Config.clsPrefix
const prefixCls = `${clsPrefix}time-panel`
const localePrefix = `${Config.localePrefix}.datepicker.`

export default {
  mixins: [Locale],

  components: {
    TimeSpinner
  },

  props: {
    visible: Boolean,
    timeArrowControl: Boolean
  },

  watch: {
    visible (val) {
      if (val) {
        this.oldValue = this.value
        this.$nextTick(() => this.$refs.spinner.emitSelectRange('hours'))
      } else {
        this.needInitAdjust = true
      }
    },

    value (newVal) {
      let date
      if (newVal instanceof Date) {
        date = limitTimeRange(newVal, this.selectableRange, this.format)
      } else if (!newVal) {
        date = this.defaultValue ? new Date(this.defaultValue) : new Date()
      }

      this.date = date
      if (this.visible && this.needInitAdjust) {
        this.$nextTick(_ => this.adjustSpinners())
        this.needInitAdjust = false
      }
    },

    selectableRange (val) {
      this.$refs.spinner.selectableRange = val
    },

    defaultValue (val) {
      if (!isDate(this.value)) {
        this.date = val ? new Date(val) : new Date()
      }
    }
  },

  data () {
    return {
      clsPrefix: clsPrefix,
      prefixCls: prefixCls,
      localePrefix: localePrefix,
      popperClass: '',
      format: 'HH:mm:ss',
      value: '',
      defaultValue: null,
      date: new Date(),
      oldValue: new Date(),
      selectableRange: [],
      selectionRange: [0, 2],
      disabled: false,
      arrowControl: false,
      needInitAdjust: true
    }
  },

  computed: {
    showSeconds () {
      return (this.format || '').indexOf('ss') !== -1
    },
    useArrow () {
      return this.arrowControl || this.timeArrowControl || false
    },
    amPmMode () {
      if ((this.format || '').indexOf('A') !== -1) return 'A'
      if ((this.format || '').indexOf('a') !== -1) return 'a'
      return ''
    }
  },

  methods: {
    handleCancel () {
      this.$emit('pick', this.oldValue, false)
    },

    handleChange (date) {
      // this.visible avoids edge cases, when use scrolls during panel closing animation
      if (this.visible) {
        this.date = clearMilliseconds(date)
        // if date is out of range, do not emit
        if (this.isValidValue(this.date)) {
          this.$emit('pick', this.date, true)
        }
      }
    },

    setSelectionRange (start, end) {
      this.$emit('select-range', start, end)
      this.selectionRange = [start, end]
    },

    handleConfirm (visible = false, first) {
      if (first) return
      const date = clearMilliseconds(limitTimeRange(this.date, this.selectableRange, this.format))
      this.$emit('pick', date, visible, first)
    },

    handleKeydown (event) {
      const keyCode = event.keyCode
      const mapping = { 38: -1, 40: 1, 37: -1, 39: 1 }

      // Left or Right
      if (keyCode === 37 || keyCode === 39) {
        const step = mapping[keyCode]
        this.changeSelectionRange(step)
        event.preventDefault()
        return
      }

      // Up or Down
      if (keyCode === 38 || keyCode === 40) {
        const step = mapping[keyCode]
        this.$refs.spinner.scrollDown(step)
        event.preventDefault()
      }
    },

    isValidValue (date) {
      return timeWithinRange(date, this.selectableRange, this.format)
    },

    adjustSpinners () {
      return this.$refs.spinner.adjustSpinners()
    },

    changeSelectionRange (step) {
      const list = [0, 3].concat(this.showSeconds ? [6] : [])
      const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : [])
      const index = list.indexOf(this.selectionRange[0])
      const next = (index + step + list.length) % list.length
      this.$refs.spinner.emitSelectRange(mapping[next])
    }
  },

  mounted () {
    this.$nextTick(() => this.handleConfirm(true, true))
    this.$emit('mounted')
  }
}
</script>
